package my.mall.admin.api.config;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;
import java.time.*;

@Configuration
public class JacksonConfig {

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {

        return builder -> {
            builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerialize(LocalDateTime.class));
            builder.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(LocalDateTime.class));
            builder.serializerByType(LocalDate.class, new LocalDateSerialize(LocalDate.class));
            builder.deserializerByType(LocalDate.class, new LocalDateDeserializer(LocalDate.class));
            builder.serializerByType(LocalTime.class, new LocalTimeSerialize(LocalTime.class));
            builder.deserializerByType(LocalTime.class, new LocalTimeDeserializer(LocalTime.class));
        };
    }

    public static class LocalDateTimeDeserializer extends StdDeserializer<LocalDateTime> {

        protected LocalDateTimeDeserializer(Class<?> vc) {
            super(vc);
        }

        @Override
        public LocalDateTime deserialize(JsonParser p, DeserializationContext ctx) throws IOException {
            long timestamp = p.getValueAsLong();
            Instant instant = Instant.ofEpochMilli(timestamp);
            ZoneId zone = ZoneId.systemDefault();
            return LocalDateTime.ofInstant(instant, zone);
        }
    }

    public static class LocalDateTimeSerialize extends StdSerializer<LocalDateTime> {

        public LocalDateTimeSerialize(Class<LocalDateTime> t) {
            super(t);
        }

        @Override
        public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator,
                              SerializerProvider serializerProvider) throws IOException {
            long timestamp = localDateTime.toInstant(OffsetDateTime.now().getOffset())
                    .toEpochMilli();
            jsonGenerator.writeNumber(timestamp);
        }
    }

    public static class LocalDateSerialize extends StdSerializer<LocalDate> {

        public LocalDateSerialize(Class<LocalDate> t) {
            super(t);
        }

        @Override
        public void serialize(LocalDate localDate, JsonGenerator jsonGenerator,
                              SerializerProvider serializerProvider) throws IOException {
            long timestamp = localDate.atStartOfDay(ZoneId.systemDefault())
                    .toInstant().toEpochMilli();
            jsonGenerator.writeNumber(timestamp);
        }
    }

    public static class LocalDateDeserializer extends StdDeserializer<LocalDate> {

        protected LocalDateDeserializer(Class<?> vc) {
            super(vc);
        }

        @Override
        public LocalDate deserialize(JsonParser p, DeserializationContext ctx) throws IOException {
            long timestamp = p.getValueAsLong();
            Instant instant = Instant.ofEpochMilli(timestamp);
            return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()).toLocalDate();
        }
    }

    public static class LocalTimeSerialize extends StdSerializer<LocalTime> {

        public LocalTimeSerialize(Class<LocalTime> t) {
            super(t);
        }

        @Override
        public void serialize(LocalTime localTime, JsonGenerator jsonGenerator,
                              SerializerProvider serializerProvider) throws IOException {
            long timestamp = localTime.atDate(LocalDate.now())
                    .atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
            jsonGenerator.writeNumber(timestamp);
        }
    }

    public static class LocalTimeDeserializer extends StdDeserializer<LocalTime> {

        protected LocalTimeDeserializer(Class<?> vc) {
            super(vc);
        }

        @Override
        public LocalTime deserialize(JsonParser p, DeserializationContext ctx) throws IOException {
            long timestamp = p.getValueAsLong();
            Instant instant = Instant.ofEpochMilli(timestamp);
            return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()).toLocalTime();
        }
    }
}
